package org.example.provider.validate;

import org.apache.dubbo.common.extension.Activate;
import org.apache.dubbo.common.utils.ConfigUtils;
import org.apache.dubbo.common.utils.StringUtils;
import org.apache.dubbo.rpc.*;
import org.apache.dubbo.validation.Validation;
import org.apache.dubbo.validation.Validator;
import org.example.api.RpcResult;

import javax.validation.ConstraintViolationException;
import javax.validation.ValidationException;

import java.util.ArrayList;
import java.util.List;

import static org.apache.dubbo.common.constants.CommonConstants.CONSUMER;
import static org.apache.dubbo.common.constants.CommonConstants.PROVIDER;
import static org.apache.dubbo.common.constants.FilterConstants.VALIDATION_KEY;

@Activate(group = {CONSUMER,PROVIDER}, value = VALIDATION_KEY, order = 10000)
public class CustomValidationFilter implements Filter {

    private Validation validation;

    /**
     * Sets the validation instance for ValidationFilter
     * @param validation Validation instance injected by dubbo framework based on "validation" attribute value.
     */
    public void setValidation(Validation validation) {
        this.validation = validation;
    }

    @Override
    public Result invoke(Invoker<?> invoker, Invocation invocation) throws RpcException {
        if (validation != null && !invocation.getMethodName().startsWith("$")
                && ConfigUtils.isNotEmpty(invoker.getUrl().getMethodParameter(invocation.getMethodName(), VALIDATION_KEY))) {
            try {
                Validator validator = validation.getValidator(invoker.getUrl());
                if (validator != null) {
                    validator.validate(invocation.getMethodName(), invocation.getParameterTypes(), invocation.getArguments());
                }
            } catch (RpcException e) {
                throw e;
            } catch (ValidationException e) {
                // only use exception's message to avoid potential serialization issue
                //return AsyncRpcResult.newDefaultAsyncResult(new ValidationException(e.getMessage()), invocation);
                String message = e.getMessage();
                if(e instanceof ConstraintViolationException){
                    ConstraintViolationException ex = (ConstraintViolationException) e;
                    List<String> messageArray = new ArrayList<String>();
                    ex.getConstraintViolations().forEach(constraintViolation -> {
                        messageArray.add(constraintViolation.getMessage());
                    });
                    message = messageArray.size() == 1 ? messageArray.get(0) : StringUtils.join(messageArray, ",");
                }
                final RpcResult<Object> rpcResult = RpcResult.error(Boolean.FALSE, message);
                return AsyncRpcResult.newDefaultAsyncResult(rpcResult, invocation);
            } catch (Throwable t) {
                return AsyncRpcResult.newDefaultAsyncResult(t, invocation);
            }
        }
        return invoker.invoke(invocation);
    }
}
